#!/bin/bash
#
# Copyright (C) Huawei Technologies., Ltd. 2021. All rights reserved.
# Description:
#     Rubik hybrid deployment performance test aims to reduce test costs,
#     construct the closest real business scenario, quickly measure the
#     efficiency and cost reduction of rubik hybrid deployment projects,
#     identify system weaknesses, and explore the enhancement and optimization
#     points and entry points of hybrid deployment characteristics
# Author: wujing
# Create: 2021-12-15

source ./utils/logs.sh

# set -euxo pipefail

# configuration
declare -g native_kubelet=""
declare -g native_scheduler=""
declare -g native_kernel=""

declare -g custom_kubelet=""
declare -g custom_scheduler=""
declare -g custom_kernel=""

declare -g rubik=""

declare -ag tasks
declare -g output=""
declare -g native_solution_bench_config="native_solution_bench.yaml"
declare -g custom_solution_bench_config="custom_solution_bench.yaml"
declare -g analysis_config="analysis_result.yaml"
declare -g k8s_build_config="k8s_build.yaml"

declare -g hybrid_deployments_package="./packages/hybrid-deployments"
declare -gr rubik_bench_config=""./config/rubikBench.yaml""

declare -g taskID=$(date "+%Y%m%d-%H%M%S")
declare -g task_lock=/tmp/rubikBench-${taskID}.lock
declare -g rubik_bench_result_dir="output/rubikBench-${taskID}"
declare -g native_test_result="native.res"
declare -g custom_test_result="custom.res"


function show_logo() {
    echo -e "\
  ██████╗ ██╗   ██╗██████╗ ██╗██╗  ██╗    ██████╗ ███████╗███╗   ██╗ ██████╗██╗  ██╗
  ██╔══██╗██║   ██║██╔══██╗██║██║ ██╔╝    ██╔══██╗██╔════╝████╗  ██║██╔════╝██║  ██║
  ██████╔╝██║   ██║██████╔╝██║█████╔╝     ██████╔╝█████╗  ██╔██╗ ██║██║     ███████║
  ██╔══██╗██║   ██║██╔══██╗██║██╔═██╗     ██╔══██╗██╔══╝  ██║╚██╗██║██║     ██╔══██║
  ██║  ██║╚██████╔╝██████╔╝██║██║  ██╗    ██████╔╝███████╗██║ ╚████║╚██████╗██║  ██║
  ╚═╝  ╚═╝ ╚═════╝ ╚═════╝ ╚═╝╚═╝  ╚═╝    ╚═════╝ ╚══════╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝"
}

function usage() {
  show_logo
  echo "Usage: ${0} [options]"
  echo "Performance comparison test between rubik hybrid deployment and native kubernetes"
  echo "Options:"
  echo "    -v, --free-verification   Install SSH authorized key on servers"
  echo "    -f, --config              RubikBentch configuration file"
  echo "    -h, --help                Print help information"
}

function environmental_detection() {
  local satisfy=true

  (which jq &>/dev/null) || (err "please install jq tool: cp ./package/local/<arch>/yq* /usr/local/bin/yq" && satisfy=false)
  (which expect &>/dev/null) || (err "please install expect tool: dnf install expect" && satisfy=false)
  (which bashful &>/dev/null) || (err "please install bashful tool: go get github.com/wagoodman/bashful" && satisfy=false)
  (which termgraph &>/dev/null) || (err "please install termgraph tool: pip install termgraph" && satisfy=false)

  ${satisfy} || exit 1
}

function parse_config() {
  # Obtain the components to be deployed, if you configure them in the configuration file, they need to be replaced
  local config=${1}

  native_kubelet=$(cat ${config} | yq e '.native.kubelet' -)
  native_scheduler=$(cat ${config} | yq e '.native.scheduler' -)
  native_kernel=$(cat ${config} | yq e '.native.kernel' -)

  custom_kubelet=$(cat ${config} | yq e '.custom.kubelet' -)
  custom_scheduler=$(cat ${config} | yq e '.custom.scheduler' -)
  custom_kernel=$(cat ${config} | yq e '.custom.kernel' -)
  custom_rubik=$(cat ${config} | yq e '.custom.rubik' -)

  # Task configuration files define the number and types of business deployments and commands
  tasks=($(cat ${config}  | yq e '.tasks' - | sed "s/- //g"))
  # Test result storage directory
  # output=$(cat ${config} | shyaml get-value output)
}

function perform_ssh_login_without_password() {
  if [[ ! -e ~/.ssh/id_rsa.pub ]]; then
    expect ./scripts/ssh-keygen.exp
  fi

  local username=$(cat ${rubik_bench_config} | yq e '.username' -)

  local username=$(cat ${rubik_bench_config} | yq e '.password' -)
  # get masters IP list
  local masterIPList=($(cat ${rubik_bench_config} | yq e ".masters" - | sed -n 's/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/\nip&\n/gp'  |grep ip | sed 's/ip//'| sort | uniq | sed '1d'))
  for element in ${masterIPList[@]}; do
    echo ${element} > /dev/null
    #expect ./scripts/ssh-copy-id.exp ${element} ${username} ${password}
  done
  # get workers IP list
  local workerIPList=($(cat ${rubik_bench_config} | yq e ".workers" -  | sed -n 's/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/\nip&\n/gp'  |grep ip | sed 's/ip//'| sort | uniq | sed '1d'))
  for element in ${workerIPList[@]}; do
    echo ${element} > /dev/null
    #expect ./scripts/ssh-copy-id.exp ${element} ${username} ${password}
  done
}

function generate_common_config() {
  echo -e "\
config:
  log-path: ./logs/native_solution_bench-${taskID}.log
  show-summary-errors: true
  show-task-output: true
  collapse-on-completion: true
  stop-on-failure: true
  success-status-color: 76
  running-status-color: 190
  pending-status-color: 237
  error-status-color: 160" > ${1}

}

function generate_prepare_workspace_task() {
  local dir=${1}
  local result=${2}
  local config=${3}
  echo -e "\
tasks:
  - name: Prepping workspace
    cmd: ./framework/prepare/prepare_workspace.sh ${dir} ${result}" >> ${config}
}

function generate_component_deploy_tasks() {
  local type=${1}
  local config=${2}
  local kernel=${3}
  local scheduler=${4}
  local kubelet=${5}

  echo -e "\

  - name: Prepping ${type} kubernetes environment
    parallel-tasks:" >> ${config}

  local masters_size=$(yq eval '.masters | length' ${rubik_bench_config})
  for ((i=0;i<${masters_size};i++)); do
    local name=$(yq eval ".masters[${i}].name" ${rubik_bench_config})
    local ip=$(yq eval ".masters[${i}].ip" ${rubik_bench_config})
    local arch=$(yq eval ".masters[${i}].arch" ${rubik_bench_config})
    # local scheduler_component=${hybrid_deployments_package}/${arch}/scheduler
    echo -e "\
      - name: Deploying ${type} kube-scheduler for ${name}
        cmd: ./framework/deploy/${type}/deploy_scheduler.sh ${scheduler} ${ip}" >> ${config}
  done

  local workers_size=$(yq eval '.workers | length' ${rubik_bench_config})
  for ((i=0;i<${workers_size};i++)); do
    local name=$(yq eval ".workers[${i}].name" ${rubik_bench_config})
    local ip=$(yq eval ".workers[${i}].ip" ${rubik_bench_config})
    local arch=$(yq eval ".workers[${i}].arch" ${rubik_bench_config})
    #local kubelet_component=${hybrid_deployments_package}/${arch}/kubelet
    #local kernel_component=${hybrid_deployments_package}/${arch}/kernel
    echo -e "\
      - name: Deploying ${type} kubelet for ${name}
        cmd: ./framework/deploy/${type}/deploy_kubelet.sh ${kubelet} ${ip}
      - name: Deploying ${type} kernel for ${name}
        cmd: ./framework/deploy/${type}/deploy_kernel.sh ${kernel} ${ip}" >> ${config}
  done

  if [[ ${type} == "native" ]]; then
    echo -e "\
      - name: Deleting Rubik
        cmd: ./framework/deploy/${type}/delete_rubik.sh" >> ${config}
  else
    echo -e "\
      - name: Deleting Rubik
        cmd: ./framework/deploy/${type}/deploy_rubik.sh" >> ${config}
  fi
}

function generate_application_deploy_tasks() {
  local config=${1}

  echo -e "\

  - name: Deploying application scenarios
    parallel-tasks:" >> ${config}
  
  for task in ${tasks[@]}; do
    local task_dir=$(find ./tasks -name ${task})
    local category=$(echo ${task_dir} | awk -F"/" '{print $(NF-1)}')
      echo -e "\
      - name: Deploying ${category} application - ${task}
        cmd: ${task_dir}/deploy.sh" >> ${config}
  done
}

function generate_stress_test_tasks() {
  local result_file=${1}
  local config=${2}
  echo -e "\

  - name: Performing stress test & Getting test results
    parallel-tasks:" >> ${config}
  
  for task in ${tasks[@]}; do
    local task_dir=$(find ./tasks -name ${task})
    local category=$(echo ${task_dir} | awk -F"/" '{print $(NF-1)}')
      echo -e "\
      - name: ${category} application stress test - ${task}
        cmd: ${task_dir}/test.sh ${result_file} ${task_lock}" >> ${config}
  done
}

function generate_application_delete_tasks() {
  local config=${1}

  echo -e "\

  - name: Deleting application scenarios
    parallel-tasks:" >> ${config}
  
  for task in ${tasks[@]}; do
    local task_dir=$(find ./tasks -name ${task})
    local category=$(echo ${task_dir} | awk -F"/" '{print $(NF-1)}')
      echo -e "\
      - name: Deleting ${category} application - ${task}
        cmd: ${task_dir}/delete.sh" >> ${config}
  done
}

function generate_analyze_task() {
  config=${1}
  echo -e "\
tasks:
  - name:  Analyzing result data
    cmd: ./framework/analysis/analysis.sh ${rubik_bench_result_dir}/${native_test_result} ${rubik_bench_result_dir}/${custom_test_result}
    show-output: true" >> ${config}
}

function generate_clean_up_workspace_task() {
  config=${1}
  echo -e "\

  - name: Cleaning up workspace
    cmd: ./framework/cleanup/cleanup_workspace.sh ${task_lock} ${analysis_config} ${native_solution_bench_config} ${custom_solution_bench_config}" >> ${config}
}

function generate_native_task() {
  generate_prepare_workspace_task ${rubik_bench_result_dir} ${native_test_result} ${native_solution_bench_config}
  generate_component_deploy_tasks "native" "${native_solution_bench_config}" "${native_kernel}" "${native_scheduler}" "${native_kubelet}"
  generate_application_deploy_tasks  ${native_solution_bench_config}
  generate_stress_test_tasks "${rubik_bench_result_dir}/${native_test_result}" ${native_solution_bench_config}
  generate_application_delete_tasks ${native_solution_bench_config}
}

function generate_native_bashful_config() {
  [[ -e ${native_solution_bench_config} ]] && rm ${native_solution_bench_config}
  touch ${native_solution_bench_config}
  mkdir -p logs
  generate_common_config ${native_solution_bench_config}
  generate_native_task
}

function execute_native_bench() {
  echo ""
  #echo "████████████████████████████████████████████████████████████████████████████(NATIVE)"
  echo -e "\033[41;37mNATIVE\033[0m\033[32m███\033[0m\033[33m███\033[0m\033[34m███\033[0m"
  bashful run ${native_solution_bench_config}
}

function generate_custom_task() {
  generate_prepare_workspace_task ${rubik_bench_result_dir} ${custom_test_result} ${custom_solution_bench_config}
  generate_component_deploy_tasks "custom" "${custom_solution_bench_config}" "${custom_kernel}" "${custom_scheduler}" "${custom_kubelet}"
  generate_application_deploy_tasks ${custom_solution_bench_config}
  generate_stress_test_tasks "${rubik_bench_result_dir}/${custom_test_result}" ${custom_solution_bench_config}
  generate_application_delete_tasks ${custom_solution_bench_config}
}

function generate_custom_bashful_config() {
  [[ -e ${custom_solution_bench_config} ]] && rm ${custom_solution_bench_config}
  touch ${custom_solution_bench_config}
  generate_common_config ${custom_solution_bench_config}
  generate_custom_task ${custom_solution_bench_config}
}

function execute_custom_bench() {
  echo ""
  #echo "████████████████████████████████████████████████████████████████████████████(CUSTOM)"
  echo -e "\033[31m███\033[0m\033[42;37mCUSTOM\033[0m\033[33m███\033[0m\033[34m███\033[0m"
  bashful run ${custom_solution_bench_config}
}

function generate_analysis_bashful_config() {
  [[ -e ${analysis_config} ]] && rm ${analysis_config}
  touch ${analysis_config}
  generate_common_config ${analysis_config}
  generate_analyze_task ${analysis_config}
  generate_clean_up_workspace_task ${analysis_config}
}

function execute_analysis_task {
  echo ""
  # echo "████████████████████████████████████████████████████████████████████████████(ANALYSIS)"
  echo -e "\033[31m███\033[0m\033[32m███\033[0m\033[43;37mANALYSIS\033[0m\033[34m███\033[0m"
  bashful run ${analysis_config}
}

function show_analysis_result() {
  echo ""
  # echo "████████████████████████████████████████████████████████████████████████████(REPORT)"
  echo -e "\033[31m███\033[0m\033[32m███\033[0m\033[33m███\033[0m\033[44;37mREPORT\033[0m"
  ./framework/analysis/analysis.sh ${rubik_bench_result_dir}/${native_test_result} ${rubik_bench_result_dir}/${custom_test_result}
}

function load_default_config() {
  parse_config "${rubik_bench_config}"
}

function main() {
  environmental_detection
  load_default_config
  args=$(getopt -o vf:h --long free-verification,config:,help -- "$@")
  if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
  eval set -- "$args"

  while true; do
    case "${1}" in
      -v|--free-verification)  perform_ssh_login_without_password || (err "failed to set authentication-free..." && exit -1); shift ;;
      -f|--config)             parse_config $2 || (err "failed to parse config..." && exit -1); shift 2 ;;
      -h|--help)               usage ; exit 0 ;;
      --)                      shift ; break ;;
      *)                       err "invalid parameter" ; exit -1 ;;
    esac
  done

  show_logo
  generate_native_bashful_config
  execute_native_bench

  generate_custom_bashful_config
  execute_custom_bench

  generate_analysis_bashful_config
  execute_analysis_task

  show_analysis_result
}

main "$@"
